---
id: building_a_blog
title: Building a blog
sidebar_label: Building a blog
description: Build a highly customizable blog
image: /img/blog_example.png
---

![blog_example](/img/blog_example.png)

:::note
In this tutorial we assume you have set up a Firebase project and a FireCMS
instance. If you don't, check the [Quickstart](firebase_setup.md) and
[Firebase setup](firebase_setup.md) sections.
:::

In this section we will break down the collection configuration we use to
build the [blog section in our demo](https://demo.firecms.co/c/blog).

We don't need to explain the benefits of using a headless CMS instead of a
traditional blogging approach like WordPress, but here are a few:
- it is easier and faster to develop on, since there is no coupling between
frontend and backend.
- since your frontend is independent, you are free to change it in any way you
want, same goes for the backend.
- it is suited for omnichannel applications, you can use the same backend and
CMS with multiple apps and websites
- smaller, specialised teams
- scalability
- less costs
- flexibility and simplicity

:::tip Frontend
When you are done implementing the steps in this tutorial, you may want to check
how to build a simple **next.js** frontend app (or any other frontend framework).
We suggest next.js since it uses React, and it will make things easier.
We are already providing many of the React components
:::

## Let's build a blog with FireCMS

We will be building a collection that hosts blog posts. Each of the blog
posts will include a **dynamic array of elements**.

FireCMS has a built-in feature that allows you to build dynamic arrays. The
array property can be configured with the prop `oneOf`, to contain objects
that hold a specific type of value. This is perfect for building our blog entry
data structure!

These are the types that we will use:
- We define the type of the blog collection:
```tsx
type BlogEntry = {
    name: string,
    header_image: string,
    created_on: Date,
    status: string,
    content: (BlogEntryImages | BlogEntryText | BlogEntryProducts)[];
}
```

- And each one of the types of the `content` array:

```tsx
type BlogEntryImages = {
    type: "images";
    value: string[];
}

type BlogEntryText = {
    type: "text";
    value: string;
}

type BlogEntryProducts = {
    type: "products";
    value: Product[]; // The Product type is coming from the quickstart
}
```

### Create the collection

Let's start by initialising our collection, without any properties:

```tsx
export const blogCollection = buildCollection<BlogEntry>({
    name: "Blog entry",
    path: "blog",
    properties: {}
});
````

### Basic properties

Then we are going to add some simple properties for our entries.

- We want to have title, that must be always set, so we set the required
prop in validation to true:

```tsx
buildProperty({
    name: "Title",
    validation: { required: true },
    dataType: "string"
})
```

- An image that will be on the top of the blog post:

```tsx
buildProperty({
    name: "Header image",
    dataType: "string",
    storage: {
        storagePath: "images",
        acceptedFiles: ["image/*"],
        metadata: {
            cacheControl: "max-age=1000000"
        }
    }
})
```

- and a "created on" date that gets **autogenerated** when the document
is created.

```tsx
buildProperty( {
    name: "Created on",
    dataType: "date",
    autoValue: "on_create"
})
```

:::note
You could also add a date field that gets updated whenever a document is saved,
with this configuration:
```tsx
buildProperty( {
    name: "Updated on",
    dataType: "date",
    autoValue: "on_update"
})
```
:::

### Conditional status field

Now we want to add a `status` string property that will have two possible values:
`published` and `draft`. We only want to allow the `published`
state when the rest of the fields are correct.

In this case we will keep it simple, and we will just check if the header image
is set:

```tsx
buildProperty(({ values }) => ({
    name: "Status",
    validation: { required: true },
    dataType: "string",
    columnWidth: 140,
    enumValues: {
        published: {
            id: "published",
            label: "Published",
            disabled: !values.header_image,
        },
        draft: "Draft"
    }
}))
```

### Content of the blog entry

The content of our blog entries needs to be **dynamic**, so that content
managers are able to create complex entries with different components.

The content will be an array of objects, that will have a `type` attribute (that
will work as a discriminator), and a `value` attribute.

We will define 3 types:
- `images`: an array of images
- `text`: a Markdown text field
- `products`: an array of references to another collection, `products` in this
case.

We use the `oneOf` prop in array properties which is designed exactly for this
use case. You just need to define

```tsx
buildProperty({
    name: "Content",
    description: "Example of a complex array with multiple properties as children",
    validation: { required: true },
    dataType: "array",
    columnWidth: 400,
    oneOf: {
        typeField: "type", // you can ommit these `typeField` and `valueField` props to use the defaults
        valueField: "value",
        properties: {
            images: buildProperty({
                name: "Images",
                dataType: "array",
                of: buildProperty({
                    dataType: "string",
                    storage: {
                        storagePath: "images",
                        acceptedFiles: ["image/*"],
                        metadata: {
                            cacheControl: "max-age=1000000"
                        }
                    }
                }),
                description: "This fields allows uploading multiple images at once and reordering"
            }),
            text: buildProperty({
                dataType: "string",
                name: "Text",
                markdown: true
            }),
            products: buildProperty({
                name: "Products",
                dataType: "array",
                of: {
                    dataType: "reference",
                    path: "products" // you need to define a valid collection in this path
                }
            })
        }
    }
})
```

This array configuration will create objects in the datasource with the format:
```json5
{
  // ...
  content: [
    {
      "type": "text",
      "value": "Sunglasses or sun glasses (informally called shades or sunnies; more names below) are a form of protective eyewear designed primarily to prevent bright sunlight and high-energy visible light from damaging or discomforting the eyes. They can sometimes function as a visual aid, as variously employed for conditions such as light sensitivity, displays, and ultraviolet protection, and to improve visual clarity. In the early 20th century, they were also known as sun cheaters (or sun glasses) and were made from cardboard, tin, and other metals, with lenses made from crudely ground glass. They were also available in the form of goggles, with improvements in technology, sunglasses have become more Earth-friendly, and the use of plastics and other modern materials is now commonplace. "
    },
    {
      "type": "images",
      "value": [
        "images/photo-1511499767150-a48a237f0083.jpeg",
        "images/photo-1577803645773-f96470509666.jpeg"
      ]
    },
    {
      "type": "text",
      "value": "Sunglasses have long been associated with celebrities and film actors primarily from a desire to mask their identity. Since the 1940s, sunglasses have been popular as a fashion accessory, especially on the beach. "
    },
    {
      "type": "products",
      "value": [
        {
          "id": "B001UQ71F0",
          "path": "products",
        },
        {
          "id": "B001UQ71F0",
          "path": "products",
        }
      ]
    }
  ],
  // ...
}
```

### Creating a preview view

Let's make use of another feature of FireCMS: **custom views for entities**!

FireCMS allows you to add additional views to your entity views, that are
defined as React components. The props you receive for building this
component are the entity collection, the original entity and the modified values.

In this case, we will create some React components to represent our blog entry
like the frontend app would do. This is the same code that you could
use in any SSR framework using React, such as `next.js`

You could also have more complex setup that sends your data to your SSR app
through an API and render the result.

You can find the code for `BlogEntryPreview` in
[BlogEntryPreview](https://github.com/FireCMSco/firecms/blob/master/example/src/docs/recipes/blog/BlogEntryPreview.tsx)

## Complete code:

If we put together all the parts that we have build in this tutorial,
we get the following code for the blog collection:


import CodeBlock from "@theme/CodeBlock";
import MyComponentSource
    from "!!raw-loader!../../../samples/samples_v2/recipes/blog/blog_collection";

<CodeBlock language="tsx">{MyComponentSource}</CodeBlock>
